#pragma once

#include "DomoticzHardware.h"

class MySensorsBase : public CDomoticzHardwareBase
{
	friend class MySensorsSerial;
	friend class MySensorsTCP;
	friend class MySensorsMQTT;
	friend class MQTT;

      public:
	enum _eMessageType
	{
		MT_Presentation = 0, // Sent by a node when they present attached sensors.This is usually done in setup() at startup.
		MT_Set = 1,	     // This message is sent from or to a sensor when a sensor value should be updated
		MT_Req = 2,	     // Requests a variable value(usually from an actuator destined for controller).
		MT_Internal = 3,     // This is a special internal message.See table "_eInternalType" below for the details
		MT_Stream = 4	     // Used for OTA firmware updates
	};

	enum _ePresentationType
	{
		S_DOOR = 0,		      // Door and window sensors	V_TRIPPED, V_ARMED
		S_MOTION = 1,		      // Motion sensors	V_TRIPPED, V_ARMED
		S_SMOKE = 2,		      // Smoke sensor	V_TRIPPED, V_ARMED
		S_LIGHT = 3,		      // Light Actuator(on / off)	V_STATUS(or V_LIGHT), V_WATT
		S_BINARY = 3,		      // Binary device(on / off), Alias for S_LIGHT	V_STATUS(or V_LIGHT), V_WATT
		S_DIMMER = 4,		      // Dimmable device of some kind	V_STATUS(on / off), V_DIMMER(dimmer level 0 - 100), V_WATT
		S_COVER = 5,		      // Window covers or shades	V_UP, V_DOWN, V_STOP, V_PERCENTAGE
		S_TEMP = 6,		      // Temperature sensor	V_TEMP, V_ID
		S_HUM = 7,		      // Humidity sensor	V_HUM
		S_BARO = 8,		      // Barometer sensor(Pressure)	V_PRESSURE, V_FORECAST
		S_WIND = 9,		      // Wind sensor	V_WIND, V_GUST
		S_RAIN = 10,		      // Rain sensor	V_RAIN, V_RAINRATE
		S_UV = 11,		      // UV sensor	V_UV
		S_WEIGHT = 12,		      // Weight sensor for scales etc.V_WEIGHT, V_IMPEDANCE
		S_POWER = 13,		      // Power measuring device, like power meters	V_WATT, V_KWH
		S_HEATER = 14,		      // Heater device	V_HVAC_SETPOINT_HEAT, V_HVAC_FLOW_STATE, V_TEMP, V_STATUS
		S_DISTANCE = 15,	      // Distance sensor	V_DISTANCE, V_UNIT_PREFIX
		S_LIGHT_LEVEL = 16,	      // Light sensor	V_LIGHT_LEVEL(uncalibrated percentage), V_LEVEL(light level in lux)
		S_ARDUINO_NODE = 17,	      // Arduino node device
		S_ARDUINO_REPEATER_NODE = 18, // Arduino repeating node device
		S_LOCK = 19,		      // Lock device	V_LOCK_STATUS
		S_IR = 20,		      // Ir sender / receiver device	V_IR_SEND, V_IR_RECEIVE
		S_WATER = 21,		      // Water meter	V_FLOW, V_VOLUME
		S_AIR_QUALITY = 22,	      // Air quality sensor e.g.MQ - 2	V_LEVEL, V_UNIT_PREFIX
		S_CUSTOM = 23,		      // Use this for custom sensors where no other fits.
		S_DUST = 24,		      // Dust level sensor	V_LEVEL, V_UNIT_PREFIX
		S_SCENE_CONTROLLER = 25,      // Scene controller device	V_SCENE_ON, V_SCENE_OFF
		S_RGB_LIGHT = 26,	      // RGB light	V_RGB, V_WATT
		S_RGBW_LIGHT = 27,	      // RGBW light(with separate white component)	V_RGBW, V_WATT
		S_COLOR_SENSOR = 28,	      // Color sensor	V_RGB
		S_HVAC = 29,		      // Thermostat / HVAC device	V_HVAC_SETPOINT_HEAT, V_HVAC_SETPOINT_COLD, V_HVAC_FLOW_STATE, V_HVAC_FLOW_MODE, V_HVAC_SPEED, V_TEMP, V_STATUS
		S_MULTIMETER = 30,	      // Multimeter device	V_VOLTAGE, V_CURRENT, V_IMPEDANCE
		S_SPRINKLER = 31,	      // Sprinkler device	V_STATUS(turn on / off), V_TRIPPED(if fire detecting device)
		S_WATER_LEAK = 32,	      // Water leak sensor	V_TRIPPED, V_ARMED
		S_SOUND = 33,		      // Sound sensor	V_LEVEL(in dB), V_TRIPPED, V_ARMED
		S_VIBRATION = 34,	      // Vibration sensor	V_LEVEL(vibration in Hz), V_TRIPPED, V_ARMED
		S_MOISTURE = 35,	      // Moisture sensor	V_LEVEL(water content or moisture in percentage ? ), V_TRIPPED, V_ARMED
		S_INFO = 36,		      // LCD text device / Simple information device on controller, V_TEXT
		S_GAS = 37,		      // Gas meter, V_FLOW, V_VOLUME
		S_GPS = 38,		      //!< GPS Sensor, V_POSITION
		S_WATER_QUALITY = 39,	      //!< V_TEMP, V_PH, V_ORP, V_EC, V_STATUS
		S_UNKNOWN = 200,	      // No Type received
	};

	enum _eSetType
	{
		V_TEMP = 0,		// Temperature	S_TEMP, S_HEATER, S_HVAC
		V_HUM = 1,		// Humidity	S_HUM
		V_STATUS = 2,		// Binary status. 0 = off 1 = on	S_LIGHT, S_DIMMER, S_SPRINKLER, S_HVAC, S_HEATER
		V_LIGHT = 2,		// Deprecated.Alias for V_STATUS.Light status. 0 = off 1 = on	S_LIGHT, S_DIMMER, S_SPRINKLER
		V_PERCENTAGE = 3,	// Percentage value. 0 - 100 (%)	S_DIMMER
		V_DIMMER = 3,		// Deprecated.Alias for V_PERCENTAGE.Dimmer value. 0 - 100 (%)	S_DIMMER
		V_PRESSURE = 4,		// Atmospheric Pressure	S_BARO
		V_FORECAST = 5,		// Whether forecast.One of "stable", "sunny", "cloudy", "unstable", "thunderstorm" or "unknown"	S_BARO
		V_RAIN = 6,		// Amount of rain	S_RAIN
		V_RAINRATE = 7,		// Rate of rain	S_RAIN
		V_WIND = 8,		// Wind speed	S_WIND
		V_GUST = 9,		// Gust	S_WIND
		V_DIRECTION = 10,	// Wind direction	S_WIND
		V_UV = 11,		// UV light level	S_UV
		V_WEIGHT = 12,		// Weight(for scales etc)	S_WEIGHT
		V_DISTANCE = 13,	// Distance	S_DISTANCE
		V_IMPEDANCE = 14,	// Impedance value	S_MULTIMETER, S_WEIGHT
		V_ARMED = 15,		// Armed status of a security sensor. 1 = Armed, 0 = Bypassed	S_DOOR, S_MOTION, S_SMOKE, S_SPRINKLER, S_WATER_LEAK, S_SOUND, S_VIBRATION, S_MOISTURE
		V_TRIPPED = 16,		// Tripped status of a security sensor. 1 = Tripped, 0 = Untripped	S_DOOR, S_MOTION, S_SMOKE, S_SPRINKLER, S_WATER_LEAK, S_SOUND, S_VIBRATION, S_MOISTURE
		V_WATT = 17,		// Watt value for power meters	S_POWER, S_LIGHT, S_DIMMER, S_RGB, S_RGBW
		V_KWH = 18,		// Accumulated number of KWH for a power meter	S_POWER
		V_SCENE_ON = 19,	// Turn on a scene	S_SCENE_CONTROLLER
		V_SCENE_OFF = 20,	// Turn of a scene	S_SCENE_CONTROLLER
		V_HVAC_FLOW_STATE = 21, // Mode of header.One of "Off", "HeatOn", "CoolOn", or "AutoChangeOver"	S_HVAC, S_HEATER
		V_HVAC_SPEED = 22,	// HVAC / Heater fan speed("Min", "Normal", "Max", "Auto")	S_HVAC, S_HEATER
		V_LIGHT_LEVEL = 23,	// Uncalibrated light level. 0 - 100 % .Use V_LEVEL for light level in lux.S_LIGHT_LEVEL
		V_VAR1 = 24,		// Custom value	Any device
		V_VAR2 = 25,		// Custom value	Any device
		V_VAR3 = 26,		// Custom value	Any device
		V_VAR4 = 27,		// Custom value	Any device
		V_VAR5 = 28,		// Custom value	Any device
		V_UP = 29,		// Window covering.Up.S_COVER
		V_DOWN = 30,		// Window covering.Down.S_COVER
		V_STOP = 31,		// Window covering.Stop.S_COVER
		V_IR_SEND = 32,		// Send out an IR - command	S_IR
		V_IR_RECEIVE = 33,	// This message contains a received IR - command	S_IR
		V_FLOW = 34,		// Flow of water(in meter)	S_WATER
		V_VOLUME = 35,		// Water volume	S_WATER, S_GAS
		V_LOCK_STATUS = 36,	// Set or get lock status. 1 = Locked, 0 = Unlocked	S_LOCK
		V_LEVEL = 37,		// Used for sending level - value	S_DUST, S_AIR_QUALITY, S_SOUND(dB), S_VIBRATION(hz), S_LIGHT_LEVEL(lux)
		V_VOLTAGE = 38,		// Voltage level	S_MULTIMETER
		V_CURRENT = 39,		// Current level	S_MULTIMETER
		V_RGB = 40,		// RGB value transmitted as ASCII hex string(I.e "ff0000" for red)	S_RGB_LIGHT, S_COLOR_SENSOR
		V_RGBW = 41,		// RGBW value transmitted as ASCII hex string(I.e "ff0000ff" for red + full white)	S_RGBW_LIGHT
		V_ID = 42,		// Optional unique sensor id(e.g.OneWire DS1820b ids)	S_TEMP
		V_UNIT_PREFIX = 43, // Allows sensors to send in a string representing the unit prefix to be displayed in GUI.This is not parsed by controller!E.g.cm, m, km, inch.S_DISTANCE, S_DUST,
				    // S_AIR_QUALITY
		V_HVAC_SETPOINT_COOL = 44, // HVAC cold setpoint(Integer between 0 - 100)	S_HVAC
		V_HVAC_SETPOINT_HEAT = 45, // HVAC / Heater setpoint(Integer between 0 - 100)	S_HVAC, S_HEATER
		V_HVAC_FLOW_MODE = 46,	   // Flow mode for HVAC("Auto", "ContinuousOn", "PeriodicOn")	S_HVAC
		V_TEXT = 47,		   // Text/Info message S_INFO
		V_CUSTOM = 48,		   // Custom messages used for controller/inter node specific commands, preferably using S_CUSTOM device type.
		V_POSITION = 49,	   // GPS position and altitude. Payload: latitude;longitude;altitude(m). E.g. "55.722526;13.017972;18"
		V_IR_RECORD = 50,	   // Record IR codes S_IR for playback
		V_PH = 51,		   //!< S_WATER_QUALITY, water PH
		V_ORP = 52,		   //!< S_WATER_QUALITY, water ORP : redox potential in mV
		V_EC = 53,		   //!< S_WATER_QUALITY, water electric conductivity ?S/cm (microSiemens/cm)
		V_VAR = 54,		   //!< S_POWER, Reactive power: volt-ampere reactive (var)
		V_VA = 55,		   //!< S_POWER, Apparent power: volt-ampere (VA)
		V_POWER_FACTOR = 56,	   //!< S_POWER, Ratio of real power to apparent power: floating point value in the range [-1,..,1]
		V_UNKNOWN = 200		   // No value received
	};

	enum _eInternalType
	{
		I_BATTERY_LEVEL = 0,	    // Use this to report the battery level(in percent 0 - 100).
		I_TIME = 1,		    // Sensors can request the current time from the Controller using this message.The time will be reported as the seconds since 1970
		I_VERSION = 2,		    // Used to request gateway version from controller.
		I_ID_REQUEST = 3,	    // Use this to request a unique node id from the controller.
		I_ID_RESPONSE = 4,	    // Id response back to sensor.Payload contains sensor id.
		I_INCLUSION_MODE = 5,	    // Start / stop inclusion mode of the Controller(1 = start, 0 = stop).
		I_CONFIG = 6,		    // Config request from node.Reply with(M)etric or (I)mperal back to sensor.
		I_FIND_PARENT = 7,	    // When a sensor starts up, it broadcast a search request to all neighbor nodes.They reply with a I_FIND_PARENT_RESPONSE.
		I_FIND_PARENT_RESPONSE = 8, // Reply message type to I_FIND_PARENT request.
		I_LOG_MESSAGE = 9,	    // Sent by the gateway to the Controller to trace - log a message
		I_CHILDREN = 10,	    // A message that can be used to transfer child sensors(from EEPROM routing table) of a repeating node.
		I_SKETCH_NAME = 11,	    // Optional sketch name that can be used to identify sensor in the Controller GUI
		I_SKETCH_VERSION = 12,	    // Optional sketch version that can be reported to keep track of the version of sensor in the Controller GUI.
		I_REBOOT = 13,		    // Used by OTA firmware updates.Request for node to reboot.
		I_GATEWAY_READY = 14,	    // Send by gateway to controller when startup is complete.
		I_REQUEST_SIGNING = 15,	    // Used between sensors when initiating signing.
		I_GET_NONCE = 16,	    // Used between sensors when requesting nonce.
		I_GET_NONCE_RESPONSE = 17,  // Used between sensors for nonce response.
		I_HEARTBEAT = 18,
		I_PRESENTATION = 19,
		I_DISCOVER = 20,
		I_DISCOVER_RESPONSE = 21,
		I_HEARTBEAT_RESPONSE = 22,
		I_LOCKED = 23,		       //!< Node is locked (reason in string-payload)
		I_PING = 24,		       //!< Ping sent to node, payload incremental hop counter
		I_PONG = 25,		       //!< In return to ping, sent back to sender, payload incremental hop counter
		I_REGISTRATION_REQUEST = 26,   //!< Register request to GW
		I_REGISTRATION_RESPONSE = 27,  //!< Register response from GW
		I_DEBUG = 28,		       //!< Debug message
		I_SIGNAL_REPORT_REQUEST = 29,  //!< Device signal strength request
		I_SIGNAL_REPORT_REVERSE = 30,  //!< Internal
		I_SIGNAL_REPORT_RESPONSE = 31, //!< Device signal strength response (RSSI)
		I_PRE_SLEEP_NOTIFICATION = 32, //!< Message sent before node is going to sleep
		I_POST_SLEEP_NOTIFICATION = 33 //!< Message sent after node woke up (if enabled)
	};

	struct _tMySensorValue
	{
		float floatValue;
		int intvalue;
		bool bValidValue;
		std::string stringValue;
		bool bFloatValue;
		bool bIntValue;
		bool bStringValue;
		time_t lastreceived;
		_tMySensorValue()
		{
			floatValue = 0;
			intvalue = 0;
			bValidValue = false;
			bFloatValue = false;
			bIntValue = false;
			bStringValue = false;
			lastreceived = 0;
		}
	};

	struct _tMySensorChild
	{
		int nodeID;
		int childID;
		int groupID;

		_ePresentationType presType;
		std::string childName;
		bool useAck;
		int ackTimeout;

		// values
		std::map<_eSetType, _tMySensorValue> values;
		// battery
		bool hasBattery;
		int batValue;

		time_t lastreceived;

		_tMySensorChild()
		{
			lastreceived = 0;
			nodeID = -1;
			childID = 1;
			groupID = 1;
			hasBattery = false;
			batValue = 255;
			presType = S_UNKNOWN;
			useAck = false;
			ackTimeout = 0;
		}
		std::vector<_eSetType> GetChildValueTypes()
		{
			std::vector<_eSetType> ret;
			for (const auto &val : values)
			{
				ret.push_back(val.first);
			}
			return ret;
		}
		std::vector<std::string> GetChildValues()
		{
			std::vector<std::string> ret;
			for (const auto &val : values)
			{
				std::stringstream sstr;
				if (val.second.bFloatValue)
				{
					sstr << val.second.floatValue;
				}
				else if (val.second.bIntValue)
				{
					sstr << val.second.intvalue;
				}
				else if (val.second.bStringValue)
				{
					sstr << val.second.stringValue;
				}
				else
				{
					sstr << "??";
				}
				ret.push_back(sstr.str());
			}
			return ret;
		}
		bool GetValue(const _eSetType vType, int &intValue) const
		{
			auto itt = values.find(vType);
			if (itt == values.end())
				return false;
			if (!itt->second.bValidValue)
				return false;
			intValue = itt->second.intvalue;
			return true;
		}
		bool GetValue(const _eSetType vType, float &floatValue)
		{
			auto itt = values.find(vType);
			if (itt == values.end())
				return false;
			if (!itt->second.bValidValue)
				return false;
			floatValue = itt->second.floatValue;
			return true;
		}
		bool GetValue(const _eSetType vType, std::string &stringValue)
		{
			auto itt = values.find(vType);
			if (itt == values.end())
				return false;
			if (!itt->second.bValidValue)
				return false;
			stringValue = itt->second.stringValue;
			return true;
		}
		void SetValue(const _eSetType vType, const int intValue)
		{
			values[vType].intvalue = intValue;
			values[vType].bValidValue = true;
			values[vType].bIntValue = true;
			values[vType].lastreceived = time(nullptr);
		}
		void SetValue(const _eSetType vType, const float floatValue)
		{
			values[vType].floatValue = floatValue;
			values[vType].bValidValue = true;
			values[vType].bFloatValue = true;
			values[vType].lastreceived = time(nullptr);
		}
		void SetValue(const _eSetType vType, const std::string &stringValue)
		{
			values[vType].stringValue = stringValue;
			values[vType].bValidValue = true;
			values[vType].bStringValue = true;
			values[vType].lastreceived = time(nullptr);
		}
	};

	struct _tMySensorSmartSleepQueueItem
	{
		int _NodeID;
		int _ChildID;
		_eMessageType _messageType;
		_eSetType _SubType;
		std::string _Payload;
		bool _bUseAck;
		int _AckTimeout;
		_tMySensorSmartSleepQueueItem(const int NodeID, const int ChildID, const _eMessageType messageType, const _eSetType SubType, const std::string &Payload, const bool bUseAck,
					      const int AckTimeout)
			: _Payload(Payload)
		{
			_NodeID = NodeID;
			_ChildID = ChildID;
			_messageType = messageType;
			_SubType = SubType;
			_bUseAck = bUseAck;
			_AckTimeout = AckTimeout;
		}
	};

	struct _tMySensorNode
	{
		int nodeID;
		std::string Name;
		std::string SketchName;
		std::string SketchVersion;
		time_t lastreceived;
		std::vector<_tMySensorChild> m_childs;
		_tMySensorNode()
		{
			lastreceived = 0;
			nodeID = -1;
		}
		_tMySensorChild *FindChildWithPresentationType(const _ePresentationType cType)
		{
			for (auto &m : m_childs)
				if (m.presType == cType)
					return &m;
			return nullptr;
		}
		_tMySensorChild *FindChildWithPresentationType(const int ChildID, const _ePresentationType cType)
		{
			for (auto &m : m_childs)
				if ((m.childID == ChildID) && (m.presType == cType))
					return &m;
			return nullptr;
		}
		_tMySensorChild *FindChildWithValueType(const int ChildID, const _eSetType valType)
		{
			for (auto &m : m_childs)
			{
				if (m.childID == ChildID)
				{
					for (const auto &val : m.values)
					{
						if (val.first == valType)
						{
							if (!val.second.bValidValue)
								return nullptr;
							return &m;
						}
					}
				}
			}
			return nullptr;
		}
		_tMySensorChild *FindChildByValueType(const _eSetType valType)
		{
			for (auto &m : m_childs)
			{
				for (const auto &val : m.values)
				{
					if (val.first == valType)
					{
						if (!val.second.bValidValue)
							return nullptr;
						return &m;
					}
				}
			}
			return nullptr;
		}
		_tMySensorChild *FindChild(const int ChildID)
		{
			for (auto &m : m_childs)
				if (m.childID == ChildID)
					return &m;
			return nullptr;
		}
	} MySensorNode;

	MySensorsBase();
	~MySensorsBase() override = default;
	bool WriteToHardware(const char *pdata, unsigned char length) override;
	_tMySensorNode *FindNode(uint8_t nodeID);
	void UpdateNode(int nodeID, const std::string &name);
	void RemoveNode(int nodeID);
	void RemoveChild(int nodeID, int childID);
	void UpdateChild(int nodeID, int childID, bool UseAck, int AckTimeout);
	static std::string GetMySensorsValueTypeStr(enum _eSetType vType);
	static std::string GetMySensorsPresentationTypeStr(enum _ePresentationType pType);
	std::string GetGatewayVersion();
	void SendTextSensorValue(int nodeID, int childID, const std::string &tvalue);

      private:
	virtual void WriteInt(const std::string &sendStr) = 0;
	void ParseData(const unsigned char *pData, int Len);
	void ParseLine(const std::string &sLine);

	void UpdateChildDBInfo(int NodeID, int ChildID, _ePresentationType pType, const std::string &Name);
	bool GetChildDBInfo(int NodeID, int ChildID, _ePresentationType &pType, std::string &Name, bool &UseAck);

	void SendCommandInt(int NodeID, int ChildID, _eMessageType messageType, bool UseAck, int SubType, const std::string &Payload);
	bool SendNodeSetCommand(int NodeID, int ChildID, _eMessageType messageType, _eSetType SubType, const std::string &Payload, bool bUseAck, int AckTimeout);
	bool SendNodeSetCommandImpl(int NodeID, int ChildID, _eMessageType messageType, _eSetType SubType, const std::string &Payload, bool bUseAck, int AckTimeout);
	void SendNodeCommand(int NodeID, int ChildID, _eMessageType messageType, int SubType, const std::string &Payload);

	void UpdateSwitch(_eSetType vType, unsigned char Idx, int SubUnit, bool bOn, double Level, const std::string &defaultname, int BatLevel);

	void UpdateSwitchLastUpdate(unsigned char NodeID, int ChildID);
	void UpdateBlindSensorLastUpdate(int NodeID, int ChildID);
	void UpdateRGBWSwitchLastUpdate(int NodeID, int ChildID);

	bool GetSwitchValue(int Idx, int SubUnit, int sub_type, std::string &sSwitchValue);

	bool GetBlindsValue(int NodeID, int ChildID, int &blind_value);

	void LoadDevicesFromDatabase();
	void Add2Database(int nodeID, const std::string &SketchName, const std::string &SketchVersion);
	void DatabaseUpdateSketchName(int nodeID, const std::string &SketchName);
	void DatabaseUpdateSketchVersion(int nodeID, const std::string &SketchVersion);

	void SendSensor2Domoticz(_tMySensorNode *pNode, _tMySensorChild *pSensor, _eSetType vType);

	void MakeAndSendWindSensor(int nodeID, const std::string &sname);

	_tMySensorNode *InsertNode(int nodeID);
	int FindNextNodeID();
	_tMySensorChild *FindSensorWithPresentationType(int nodeID, _ePresentationType presType);
	_tMySensorChild *FindChildWithValueType(int nodeID, _eSetType valType, int groupID);
	void UpdateNodeBatteryLevel(int nodeID, int Level);
	void UpdateNodeHeartbeat(uint8_t nodeID);

	void UpdateVar(int NodeID, int ChildID, int VarID, const std::string &svalue);
	bool GetVar(int NodeID, int ChildID, int VarID, std::string &sValue);

	bool StartSendQueue();
	void StopSendQueue();
	void Do_Work();

      private:
	std::string m_szSerialPort;
	std::map<int, _tMySensorNode> m_nodes;
	concurrent_queue<std::string> m_sendQueue;
	std::shared_ptr<std::thread> m_thread;
	std::string m_GatewayVersion;
	bool m_bAckReceived;
	int m_AckNodeID;
	int m_AckChildID;
	_eSetType m_AckSetType;
	std::string m_LineReceived;
	std::map<int, bool> m_node_sleep_states;
	std::map<int, std::vector<_tMySensorSmartSleepQueueItem>> m_node_sleep_queue;
	std::mutex m_node_sleep_mutex;
};
